#include "serveroperation.h"
#include <QThreadPool>
#include "requestfactory.h"
#include "jsonparse.h"
#include "respondfactory.h"
#include "aescrypto.h"
#include <QCryptographicHash>
#include <QDebug>


AcceptConn::AcceptConn()
{
    // 当前对象在线程池中任务执行完毕, 自动析构
    setAutoDelete(true);
    // 对象实例化, 监听, 接受客户端连接请求, 建立新连接
    m_server = new TcpServer;
    // 设置监听
    // 需要从配置文件中读出服务器端绑定的端口
    SerConfig conf = JsonParse::getInstance()->readServerConfig();
    m_server->setListen(conf.port.toInt());
    // 实例化共享内存对象
    m_shm = new SeckeyShm(conf.shmKey, conf.shmMaxNode.toInt());
}

AcceptConn::~AcceptConn()
{
    delete m_server;
    delete m_shm;
}

void AcceptConn::run()
{
    // 当前类的主要的任务 --> 循环检测有没有新的连接
    // 这是一死循环, 这个对象会一直工作,该对象在线程池中占用的线程也就会一直工作
    // 主线程退出, 子线程是不会退出的, 因此需要在合适的时机终止这个死循环
    while(1)
    {
        if(m_stop)
        {
            break;
        }
        TcpSocket* tcp = m_server->acceptConn(5);
        if(tcp == NULL)
        {
            // 对象创建失败, 让客户端重试即可
            emit sendMsg("连接超时, 重试...");
            continue;
        }
        emit sendMsg("成功和客户端建立了连接...");
        // 成功建立了连接, 处理通信流程, 对应一个任务对象
        // 将这个对象添加到线程池中, 线程池会给这个任务分配一个处理的线程
        Communication* task = new Communication(tcp, m_shm);
        QThreadPool::globalInstance()->start(task);
        // 信号中转 -> 通过信号连接信号
        connect(task, &Communication::sendMsg, this, &AcceptConn::sendMsg);
    }
}

void AcceptConn::stop()
{
    m_stop = true;
}



//////////////////////////////////////////////
/////////////   和客户端通信的类    /////////////
//////////////////////////////////////////////
Communication::Communication(TcpSocket* tcp, SeckeyShm* shm)
{
    // 当前对象在线程池中任务执行完毕, 自动析构
    setAutoDelete(true);
    // 创建/关联共享内存
    SerConfig conf = JsonParse::getInstance()->readServerConfig();
    // 实例化数据库对象
    m_db = new DBOperation;
    // 连接数据库
    bool bl = m_db->connectDB(conf.dbUserName, conf.dbPasswd, conf.dbHost);
    qDebug() << "数据连接状态: " << bl;
    m_tcp = tcp;
    m_shm = shm;
}

Communication::~Communication()
{
    qDebug() << "我是通信的任务对象, 我被析构了...";
}

void Communication::seckeyConsult(RequestInfo* reqMsg)
{
    emit sendMsg("客户端请求和服务器进行秘钥协商...");
    // 先去校验客户端身份/数据, 然后进行业务流程处理
    // 最终目的生成一个对称加密的秘钥, 并且加密回复给客户端:
    // 1. 编写一个生成秘钥字符串的函数
    // 2. 加密方式: 非对称加密, 并且需要使用公钥加密
    // 3. 公钥是客户端发送给服务器的, 得到了公钥字符串, 使用的时候需要是以RSA对象
    ////////////////////////////////////
    // 1. 将公钥字符串转换为RSA对象
    RsaCrypto rsa;
    rsa.parseStringToKey(reqMsg->data);
    // 2. 签名或者身份的校验
    bool bl = identifyVerify(rsa, reqMsg);
    RespondInfo resInfo;
    if(!bl)
    {
        // 校验失败了
        resInfo.status = false;
        emit sendMsg("客户端身份信息或者是签名校验失败了...");
    }
    else
    {
        emit sendMsg("客户端身份信息或者是签名校验成功了...");
        // 生成aes的秘钥字符串
        QString key = generateAesKey(L16);
        emit sendMsg("生成AES秘钥: " + key);

        // 将秘钥信息写入到共享内存中
        int keyID = m_db->getKeyID();
        SeckeyInfo keyinfo(true, keyID, reqMsg->clientID, reqMsg->serverID, key);

        // 将秘钥信息写入到数据库中
        emit sendMsg("将协商的秘钥被写入了共享内存和数据库中...");
        bl = m_db->insertSeckeyInfo(keyinfo);
        if(bl)
        {
            // 写数据成功了
            m_shm->writeShm(keyinfo);
            resInfo.status = true;
            // {"KeyID":"xxx", "AesKey":"xxx"}
            // 使用公钥对生成的秘钥加密, 得到密文, 将这个密文回复给客户端
            QStringList keys, values;
            keys << "KeyID" << "AesKey";
            values << QString::number(keyID) << rsa.rsaPubKeyEncrypt(key);
            resInfo.data = JsonParse::getInstance()->generateSimpleObj(keys, values);

            emit sendMsg("秘钥协商成功, 恭喜, 哈哈哈...");

            // 生成外联接口使用的配置文件
            // 准备数据
            keys.clear();
            values.clear();
            keys << "ShmKey" << "ServerID" << "ClientID";
            // 在服务器端, 客户端的ID是在配置文件中保留, 但是不使用
            SerConfig conf = JsonParse::getInstance()->readServerConfig();
            values << conf.shmKey << conf.serverID << "";
            JsonParse::getInstance()->writeInterfaceJson(keys, values);
        }
        else
        {
            resInfo.status = false;
            emit sendMsg("秘钥协商失败了, 非常遗憾, 呜呜呜...");
        }
    }
    // 回复的数据需要序列化
    sendSerializeMsg(&resInfo, "秘钥协商操作完毕, 拜拜...");
}

void Communication::seckeyVerify(RequestInfo* reqMsg)
{
    // 1. 得到pem的公钥字符串 -> RSA类型, 进行签名的校验
    // 公钥在reqMsg 对象中, 并且是 Json 对象字符串
    // keys << "PubKey" << "Hash";
    JsonParse* json = JsonParse::getInstance();
    QString hash = json->simpleObjValue(reqMsg->data, "Hash");
    QString pubKey = json->simpleObjValue(reqMsg->data, "PubKey");

    // 2. 校验客户端身份
    RsaCrypto rsa;
    rsa.parseStringToKey(pubKey);
    bool bl = identifyVerify(rsa, reqMsg);

    // 3. 判断
    RespondInfo resInfo;
    if(!bl)
    {
        // 失败了
        resInfo.status = false;
    }
    else
    {

        // 成功了
        // 读共享内存中的秘钥, 计算秘钥的哈希值, 和客户端的哈希进行比较
        SeckeyInfo keyinfo = m_shm->readShm(reqMsg->clientID, reqMsg->serverID);
        // 计算秘钥的sha224
        QString sha224 = QCryptographicHash::hash(
                    keyinfo.seckey, QCryptographicHash::Sha224);
        // 比较哈希值
        if(sha224 == hash)
        {
            resInfo.status = true;
        }
        else
        {
            resInfo.status = false;
        }
    }

    // 序列化回复的数据, 发送给客户端
    sendSerializeMsg(&resInfo, "秘钥校验操作完毕, 拜拜...");
}

void Communication::seckeyLogout(RequestInfo* reqMsg)
{
    emit sendMsg("客户端请求进行秘钥状态修改...");
    // 解析json字符串得到 公钥
    // {"KeyID":"123", "Status":"1", "PubKey":"xxxyyz"}
    JsonParse* json = JsonParse::getInstance();
    int keyID = json->simpleObjValue(reqMsg->data, "KeyID").toInt();
    QString status = json->simpleObjValue(reqMsg->data, "Status");
    QString pubKey = json->simpleObjValue(reqMsg->data, "PubKey");

    RsaCrypto rsa;
    // 通过pem格式公钥字符串初始化 rsa 对象
    emit sendMsg("开始校验客户端身份和数字签名...");
    rsa.parseStringToKey(pubKey);
    // 客户端身份和数据校验
    RespondInfo info;
    bool bl = identifyVerify(rsa, reqMsg);
    if(!bl)
    {
        info.status = false;
        emit sendMsg("客户端身份或数字签名校验失败!!!");
    }
    else
    {
#if 1
        emit sendMsg("客户端身份或数字签名校验成功!!!");
        // 修改数据库中秘钥的状态
        bl = m_db->modifyKeyStatus(keyID, status.toInt());
        // 修改共享内存中秘钥状态
        // 从共享内存中读出秘钥信息
        if(bl)
        {
            SeckeyInfo keyinfo = m_shm->readShm(keyID);
            keyinfo.status = status.toInt();
            m_shm->writeShm(keyinfo);

            info.status = true;
            emit sendMsg("秘钥注销: 恭喜, 成功了, 当前秘钥状态: " + status);
        }
        else
        {
            info.status = false;
            emit sendMsg("秘钥注销: 非常遗憾, 秘钥状态修改失败...");
        }
#endif
    }
    sendSerializeMsg(&info, "秘钥注销操作完毕, 拜拜...");
}

void Communication::clientLogin(RequestInfo *reqMsg)
{
    // 1. 读共享内存, 将加密的数据解密
    SeckeyInfo keyinfo = m_shm->readShm(reqMsg->clientID, reqMsg->serverID);
    AesCrypto aes(keyinfo.seckey);
    QString decStr = aes.aesCBCDecrypt(reqMsg->data);

    // 2. 解析Json字符串: json对象
    // {"NetID":"xxx", "Password":"xxxx", "PubKey":"xxxx"}
    JsonParse* json = JsonParse::getInstance();
    QString netid = json->simpleObjValue(decStr, "NetID");
    QString passwd = json->simpleObjValue(decStr, "Password");
    QString pubkey = json->simpleObjValue(decStr, "PubKey");

    // 3. 校验客户端身份
    RsaCrypto rsa;
    rsa.parseStringToKey(pubkey);
    bool bl = identifyVerify(rsa, reqMsg);

    // 4. 判断
    RespondInfo resInfo;
    if(!bl)
    {
        // 失败了
        resInfo.status = false;
    }
    else
    {

        // 成功了, 查询数据库, 看客户端的ID和密码是否正确
        bool flag = m_db->userAuthentication(netid, passwd);
        resInfo.status = flag;
    }

    // 序列化回复的数据, 发送给客户端
    sendSerializeMsg(&resInfo, "登录操作完毕, 拜拜...");
}

void Communication::run()
{
    // 接收客户端的数据
    QString recvStr = m_tcp->recvMsg();
    emit sendMsg("接收秘钥协商客户端的请求数据...");
    // 对请求数据进行反序列化
    // 创建共享对象
    CodecFactory* f = new RequestFactory(recvStr);
    Codec* c = f->createCodec();
    // 反序列化
    RequestInfo reqInfo;
    c->decodeMsg(&reqInfo);
    delete f;
    delete c;
    emit sendMsg("客户端请求数据反序列化完成...");

    // 判断客户端请求的类型
    switch(reqInfo.cmdtype)
    {
    case Consult:
        seckeyConsult(&reqInfo);
        break;
    case Verify:
        seckeyVerify(&reqInfo);
        break;
    case Logout:
        seckeyLogout(&reqInfo);
        break;
    case Login:
        clientLogin(&reqInfo);
    default:
        break;
    }
}

bool Communication::identifyVerify(RsaCrypto &rsa, RequestInfo *reqMsg)
{
    // 1. 校验客户端的身份信息(数据库查询)
    bool flag = m_db->isValideClient(reqMsg->clientID);
    // 2. 对签名进行校验-> 和签名的过程应该是对应的
    QByteArray sha1 = QCryptographicHash::hash(
                reqMsg->data.toUtf8(), QCryptographicHash::Sha1);
    bool bl = rsa.rsaVerify(sha1, reqMsg->sign);
    // 3. 获取当前服务器的ID, 和客户端发送的ID进行比对
    SerConfig conf = JsonParse::getInstance()->readServerConfig();
    if(bl && reqMsg->serverID == conf.serverID && flag)
    {
        return true;
    }
    return false;
}

// 秘钥字符串中都包含按下字符: a-z, A-Z, 0-9, 特殊字符(!~@#$%^&*()_+=)
QString Communication::generateAesKey(Communication::KeyLen len)
{
    char cc;
    QByteArray retValue;
    QByteArray tmp("!~@#$%^&*()_+=");
    qsrand(time(NULL));
    // 需要多少个字符, 循环多少次, 将每次生成的字符保存起来
    for(int i=0; i<len; ++i)
    {
        int flag = qrand() % 4; // 0, 1, 2, 3
        switch(flag)
        {
        case 0: // a-z
            cc = qrand() % 26 + 'a';
            break;
        case 1: // A-Z
            cc = qrand() % 26 + 'A';
            break;
        case 2: // 0-9
            cc = qrand() % 10 + '0';
            break;
        case 3: // 特殊字符
            cc = tmp.at(qrand() % tmp.size());
            break;
        }
        retValue.append(cc);
    }
    return retValue;
}

void Communication::sendSerializeMsg(RespondInfo *info, QString log)
{
    CodecFactory* f = new RespondFactory(info);
    Codec* c = f->createCodec();
    QString msg = c->encodeMsg();
    emit sendMsg("给客户端回复数据...");
    m_tcp->sendMsg(msg);
    delete f;
    delete c;
    m_tcp->disConnect();    // 短连接, 通信完成断开连接
    delete m_tcp;
    emit sendMsg(log);
    emit sendMsg("====================== The End =====================");
}
